package com.nulldev.util.web.HttpClient;

import java.net.Proxy;
import java.net.URL;
import java.time.Duration;
import java.util.List;
import java.util.Map;

import com.nulldev.util.concurrency.threadIt.v4.emapi.uft.Executable;
import com.nulldev.util.data.Variables;
import com.nulldev.util.web.URLUtils.Entry;

public interface HttpRequest<T> extends HttpClientFlags<HttpRequest<T>> {

	public HttpRequest<T> url(final String url) throws Exception;

	public HttpRequest<T> url(final URL url) throws Exception;

	public HttpRequest<T> header(final String key, final String value);

	public HttpRequest<T> header(final Entry entry);

	public HttpRequest<T> headers(final Entry[] entries);

	public default HttpRequest<T> headers(final List<Entry> entries) {
		Variables.requireNonNull(entries, "entries");
		for (final Entry entry : entries)
			header(entry);
		return this;
	}

	public default HttpRequest<T> headers(final Map<String, String> headers) {
		if (headers == null || headers.isEmpty())
			return this;
		headers.forEach((k, v) -> {
			this.header(k, v);
		});
		return this;
	}

	public HttpRequest<T> forceHeader(String key, String value);

	public HttpRequest<T> timeout(final Duration duration);

	public default HttpResponse<T> create() throws Exception {
		return create(HttpMethod.GET);
	}

	public default HttpResponse<T> create(final HttpMethod method) throws Exception {
		return create(method, null);
	}

	public HttpResponse<T> create(final HttpMethod method, final Object body) throws Exception;

	public default Executable<HttpResponse<T>> createTask() throws Exception {
		return createTask(HttpMethod.GET);
	}

	public default Executable<HttpResponse<T>> createTaskAndRun() throws Exception {
		return createTaskAndRun(HttpMethod.GET);
	}

	public default Executable<HttpResponse<T>> createTask(final HttpMethod method) throws Exception {
		return createTask(HttpMethod.GET, null);
	}

	public default Executable<HttpResponse<T>> createTaskAndRun(final HttpMethod method) throws Exception {
		return createTaskAndRun(HttpMethod.GET, null);
	}

	public Executable<HttpResponse<T>> createTask(final HttpMethod method, final Object body) throws Exception;

	public default Executable<HttpResponse<T>> createTaskAndRun(final HttpMethod method, final Object body) throws Exception {
		return HttpClient.gld.asyncSubmit(this.createTask(method, body));
	}

	public boolean isSupportedMethod(final HttpMethod method);

	public HttpRequest<T> enableBuffering(final int bufferSize);

	public default HttpRequest<T> proxy(final Proxy proxy) {
		return this;
	}

	public ResponseFormat getFormat();

	public default HttpRequest<T> preferVersion(final Version httpVer) {
		return this;
	}

	public default HttpRequest<T> requestCaching(final CachingRequestType type) {
		return this;
	}

	public default HttpRequest<T> setFeatureFlag(final int feature, final Object value) {
		return this;
	}

	public default HttpRequest<T> tryFeatureFlag(final int feature, final Object value) {
		if (!this.isFeatureFlagSupported(feature))
			return this;
		try {
			this.setFeatureFlag(feature, value);
		} catch (Exception ex) {
		}
		return this;
	}

	/* Read API */

	public String url();

	public Map<String, List<String>> headers();
}
